package plgutils

import (
	"dgo/MyProj/PrjDBUtils"
	"dgo/MyProj/PrjHttpClt"
	"dgo/MyProj/PrjHttpUtils"
	"dgo/goutils/utils"
	"dgo/plg/plgbase"
	"dgo/plg/plgnet"
	"fmt"
	"gitee.com/ymofen/gobase/golog"
	"gitee.com/ymofen/supervalue"
	"sync/atomic"
	"time"
)

/*
 */
type PlgExtractList struct {
	workflag int32
	PlgBase
	last_reload_t    time.Time
	templ_parse_ver  int
	checkremove_flag int
	msglst           *utils.SyncCycleList
	timer            *utils.Timer
	filename_json    string
	datasource       string
	srctxtData       string
	srctxtRowSep     string
	srctxtValSep     string
	srcJSON          *wrapper.SuperValue
	srcPlg           *PlgBaseInnerPlug
	srcPlgArgs       string
	sql              string
	url              string

	lastData string

	itemRenameFields string
	itemRemoveFields string
	itemTempl        *wrapper.SuperValue

	firstdelay time.Duration

	outputPlg *PlgBaseInnerPlug

	warn string
}

func (this *PlgExtractList) String() string {
	return fmt.Sprintf("PlgExtractList-%s", this.PlugId)
}

func (this *PlgExtractList) innerDoFromSrcPlg() {
	obj := this.srcPlg.CheckGetPlug()
	if obj == nil {
		return
	}

	var json *wrapper.SuperValue
	if lvIntf, ok := obj.(utils.IRequestIntf); ok {
		resp := lvIntf.DoRequest(this.srcPlgArgs)
		if resp != nil {
			json, _ = wrapper.NewSVFrom(resp, false)
		}
	}
	if json != nil {
		if intf, ok := obj.(wrapper.IFillJsonStatus); ok {
			json = wrapper.NewSVArray()
			intf.FillJsonStatus(json, this.srcPlgArgs)
		}
	}

	if json != nil {
		this.innerDoFromJSON(json)
	} else {
		this.warn = fmt.Sprintf("[%s][%s]无法获取JSON数据", utils.NowString(), this.srcPlg.StatusString())
	}

}

func (this *PlgExtractList) innerDoFromDB() {
	sql := ReplacePlaceholderForFileName(this.sql)
	dao := PrjDBUtils.NewDAOHelper(this.datasource)
	defer dao.Close()

	json := wrapper.NewSVArray()

	_, err := dao.QueryData(sql, json, nil)
	if err != nil {
		golog.Warnf("获取数据:%s异常, %s", sql, err.Error())
		return
	}
	this.innerDoFromJSON(json)
}

func (this *PlgExtractList) innerDoFromUrl() {
	url := ReplacePlaceholderForFileName(this.url)
	json, err := PrjHttpClt.GetUrlAsJSON(url)
	if err != nil {
		golog.Warnf("获取URL:%s异常, %s", url, err.Error())
		return
	}
	this.innerDoFromJSON(json)
}

func (this *PlgExtractList) innerDoFromJsonFile() {
	fn := ReplacePlaceholderForFileName(this.filename_json)
	json, err := wrapper.NewSVFromFile(fn)
	if err != nil {
		golog.Warnf("获取数据:%s异常, %s", fn, err.Error())
		return
	}
	this.innerDoFromJSON(json)
}

func (this *PlgExtractList) ReloadList(lst *wrapper.SuperValue) {
	if lst == nil {
		return
	}
	this.innerDoFromJSON(lst)
}

func (this *PlgExtractList) innerDoFromJSON(json *wrapper.SuperValue) {
	var lst *wrapper.SuperValue
	if json.IsArray() {
		lst = json
	} else {
		lst = json.ValueByName("list")
	}

	if lst == nil {
		return
	}

	lst.Range(func(key string, val *wrapper.SuperValue) bool {
		if len(this.itemRemoveFields) > 0 {
			val.RemoveFields(this.itemRemoveFields)
		}

		if len(this.itemRenameFields) > 0 {
			val.RenameByFields(this.itemRenameFields)
		}

		val.CopyFromIfNotExists(this.itemTempl)

		return true
	})

	str := lst.AsJSONStringEx(true, false)
	if str != this.lastData {
		this.lastData = str
		lvPlg := this.outputPlg.CheckGetPlug()
		if lvIntf, ok := lvPlg.(wrapper.IReloadList); ok {
			lvIntf.ReloadList(lst)
		}
	}
}

func (this *PlgExtractList) innerDo() {
	if !this.last_reload_t.IsZero() && time.Since(this.last_reload_t) < this.timer.GetInterval() {
		return
	}

	if !atomic.CompareAndSwapInt32(&this.workflag, 0, 1) {
		return
	}
	defer func() {
		atomic.StoreInt32(&this.workflag, 0)
		this.last_reload_t = time.Now()
	}()

	if this.srcPlg.IsValid() {
		this.innerDoFromSrcPlg()
	} else if this.srcJSON != nil {
		this.innerDoFromJSON(this.srcJSON)
	} else if len(this.srctxtData) > 0 {
		json := wrapper.NewSVArray()
		wrapper.TextData2SVArray(this.srctxtData, json, "\n", ",", true)
		this.innerDoFromJSON(json)
	} else if len(this.sql) > 0 {
		this.innerDoFromDB()
	} else if len(this.filename_json) > 0 {
		this.innerDoFromJsonFile()
	} else if len(this.url) > 0 {
		this.innerDoFromUrl()
	} else {
		this.warn = fmt.Sprintf("[%s]无法获取数据源，请检查配置")
	}

}

/*
*

{
"id": "plg-maker",
"type": "utils.plug.maker.mix",
"conf": {
"srcPlg":"",
"srcPlgArgs":"",

"filename-json":"",
"src-txtdata":"id\nBJF1\n",
"src-json":[{}],

"url":"",

"datasource":"main",
"sql":"select * from ",

"interval":30,
"item-templ":{},
"item-rename":"",

"output":{}
}
}

filename-json: 从文件中读取JSON

"datasource":"main",
"sql":"select * from ",  // 从数据表中读取
*/
func (this *PlgExtractList) ReloadConf(conf *wrapper.SuperValue) error {
	if conf == nil {
		return nil
	}

	if this.timer == nil {
		this.timer = utils.NewTimer()
		this.timer.OnTimer = func(tag interface{}) {
			utils.DefaultWorkers().PostTaskFunc(this, this.innerDo)
		}
		this.timer.ConfigInterval(60*time.Second, 0)
	}

	if this.msglst == nil {
		this.msglst = utils.NewSyncCycleList()
	}

	conf.CheckExistsIntFunc("interval", func(val int64) {
		this.timer.ConfigInterval(time.Second*time.Duration(val), 0)
	})

	conf.CheckExistsIntFunc("reload-interval-secs", func(val int64) {
		this.timer.ConfigInterval(time.Second*time.Duration(val), 0)
	})

	conf.CheckExistsStrFunc("item-templ", func(strv string) {
		strv = utils.ReplacePlaceholder(strv, "$$", "$$")
		this.itemTempl, _ = wrapper.NewSVFrom(strv, false)
	})

	conf.CheckExistsStrFunc("item-renamefields", func(strv string) {
		strv = utils.ReplacePlaceholder(strv, "$$", "$$")
		this.itemRenameFields = strv
	})

	conf.CheckExistsStrFunc("item-removefields", func(strv string) {
		strv = utils.ReplacePlaceholder(strv, "$$", "$$")
		this.itemRemoveFields = strv
	})

	isChanged := false

	if this.srcPlg == nil {
		this.srcPlg = PlgNewInnerPlugEx(this)
	}
	this.srcPlg.ReloadConf(conf.ValueByName("srcPlg"))
	this.srcPlgArgs = utils.ReplacePlaceholder(conf.StringByName("srcPlgArgs", ""), "$$", "$$")

	if this.outputPlg == nil {
		this.outputPlg = PlgNewInnerPlugEx(this)
	}
	this.outputPlg.ReloadConf(conf.ValueByName("output"))

	lst := conf.ValueByName("src-json")
	if lst != nil {
		tmp := lst.Clone()
		if this.srcJSON == nil {
			this.srcJSON = tmp
			isChanged = true
		} else {
			if tmp.String() != this.srcJSON.String() {
				this.srcJSON = tmp
				isChanged = true
			}
		}
	}

	conf.CheckStrFunc("filename-json", func(strv string) {
		strv = utils.ReplacePlaceholder(strv, "$$", "$$")
		if this.filename_json != strv {
			this.filename_json = strv
			isChanged = true
		}
	})

	conf.CheckExistsStrFunc("src-txtdata", func(strv string) {
		if this.srctxtData != strv {
			this.srctxtData = strv
			isChanged = true
		}
	})

	conf.CheckStrFunc("sql", func(strv string) {
		this.datasource = conf.StringByName("datasource", "main")
		strv = utils.ReplacePlaceholder(strv, "$$", "$$")
		if this.sql != strv {
			this.sql = strv
			isChanged = true
		}
	})

	conf.CheckStrFunc("url", func(strv string) {
		strv = utils.ReplacePlaceholder(strv, "$$", "$$")
		if this.url != strv {
			this.url = strv
			isChanged = true
		}
	})

	plgbase.AddSysReloadNotify(this, func() {
		utils.DefaultWorkers().PostTaskFunc(this, this.innerDo)
	})

	if isChanged {
		loaddelay := time.Duration(conf.Int64ByName("load-delay-secs", 20)) * time.Second
		if loaddelay <= 0 {
			this.innerDo()
		} else {
			time.AfterFunc(loaddelay, func() {
				utils.DefaultWorkers().PostTaskFunc(this.PlugId, this.innerDo)
			})
		}
	}

	return nil
}

func (this *PlgExtractList) LogInfoMessage(s string, args ...interface{}) {
	this.msglst.LogMsg(s, args...)
	utils.Log.Msgf(this.PlugId, utils.LogLV_INFO, 1, s, args...)
}

func (this *PlgExtractList) LogWarnMessage(s string, args ...interface{}) {
	this.msglst.LogMsg(s, args...)
	utils.Log.Msgf(this.PlugId, utils.LogLV_WARN, 1, s, args...)
}

func (this *PlgExtractList) OnHttpStatus(rec *plgnet.PlgHttpRec) (rval interface{}, errcode int, err error) {
	itype := rec.QryAsInt("type", 0)
	if itype == 99 {
		json := wrapper.NewSVObject()
		json.SetKeyValue("templ-parse-ver", this.templ_parse_ver)
		if this.srcPlg.IsValid() {
			json.SetKeyValue("srcPlg", this.srcPlg.StatusString())
			json.SetKeyValue("srcPlgArgs", this.srcPlgArgs)
		}

		if len(this.datasource) > 0 {
			json.SetKeyValues("sql", fmt.Sprintf("[%s]:%s", this.datasource, this.sql))
		}
		json.SetKeyValues("url", this.url)
		json.SetKeyValues("file", this.filename_json)

		json.SetKeyValues("now", utils.NowString())

		if rec.QryAsInt("json", 0) == 1 {
			PrjHttpUtils.ResponseAsJSON(rec.RespW, json.AsJSONStringEx(false, true))
		} else {
			PrjHttpUtils.JSON2TableRec(rec.RespW, "", json.AsJSONString(false))
		}
	} else if itype == 98 {
		rec.ResponseSyncListNEx(this.msglst)
	} else if itype == 1 {
		//searchval := rec.QryAsString("searchval", "")
		//json := wrapper.NewSVArray()
		//this.lstDevice.ListRangeV2(func(key, value interface{}) bool {
		//	//dev := value.(*YsledMqttWebServDevice)
		//	ok := true
		//	if len(searchval) > 0 {
		//
		//	}
		//	return ok
		//}, func(k0, v0, k1, v1 interface{}) bool {
		//	return true
		//}, func(key, val interface{}) bool {
		//	return true
		//})
		//
		//if rec.QryAsInt("json", 0) == 1 {
		//	PrjHttpUtils.ResponseAsJSON(rec.RespW, json.AsJSONStringEx(false, false))
		//} else {
		//	PrjHttpUtils.JSON2Table(rec.RespW, "", json.AsJSONString(false))
		//}
	} else if itype == 2 {
		rec.ResponseText(this.lastData)
	} else {
		var sb utils.BytesBuilder
		PrjHttpUtils.AppendIndexBody(&sb, "status", "status?type=99")
		PrjHttpUtils.AppendIndexBody(&sb, "msglist", "status?type=98&n=100&searchval=")
		PrjHttpUtils.AppendIndexBody(&sb, "lastRaw", "status?type=2")
		PrjHttpUtils.ResponseBodyHtml(rec.RespW, sb.String())
	}
	return
}

func (this *PlgExtractList) OnObjectNotify(sender interface{}, notifytype int, data interface{}) (rval interface{}, err error) {
	if notifytype == utils.NOTIFY_TYPE_SETCONF {
		if conf, ok := data.(*wrapper.SuperValue); ok {
			err = this.ReloadConf(conf)
		} else {
			err = fmt.Errorf("非法的JSON配置")
		}
	} else if notifytype == utils.NOTIFY_TYPE_RELEASE {
		this.ReleaseFlag = 1
		this.timer.Close()
		plgbase.DelSysReloadNotify(this)
	}
	return
}

func CreatePlgExtractListFunc(args ...interface{}) (rval interface{}, err error) {
	obj := &PlgExtractList{}
	if len(args) > 0 {
		if str, ok := args[0].(string); ok {
			obj.PlugId = str
		}
	}

	rval = obj
	return
}
